-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Conversation
…duces complexity significantly and removes the process.exit() ugliness.
…This commit relies on the development version of ether-pudding, which isn't released yet.
…the deployment has started. Also, fix a bug in the linker, as well as other refactorings.
…coupling. Refactoring not finished; many modules likely broken.
…loy code that no longer needs to exist.
…contracts provisioner so we don't have to write the same code all the time (i.e., tests and migrations).
… anything if there's nothing to link.
…passed as parameters. Also allow deploy() to take an array of contracts, or an array of array, which will deploy those contracts in parallel, speeding up that migration.
…fle. 'compile', 'migrate' and 'build' commands added to REPL for ease of use.
…truffle networks`
…r Truffles). Tests are updated and new ones added. This resulted in some nice refactorings elsewhere.
…their own modules. Severely beef up console: Now you can enter any normal console command and work with your contracts directly. Add checks to ensure certain functions receive the options they expect.
…wly deployed/compiled contracts.
…r executed immediately; pull out deferred chain so it's less complicated and not coupled to deployer. Other files: various fixes/improvements.
The deployer API makes a lot of sense. Feature-wise it seems similar to DappleScript but written in pure Javascript (which seems like a win). Would you say that's accurate? The networks feature looks great. Totally support embedding network data inside of I don't know if I fully understand how the migrations feature works. It might be useful to add a diagram which can describe how the migrations stuff works, like what information is stored in Javascirpt, and what's on chain. |
function upgrade(address new_address) restricted { | ||
Migrations upgraded = Migrations(new_address); | ||
upgraded.setCompleted(last_completed_migration); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this works the way you want it to. The default authentication scheme only allows setCompleted
to be called by the owner
, but the old Migrations
is not going to be the owner. Granted your new code might be different, but seems weird to have to write a new Migrations
contract with the assumption that the old one can call it.
I don't think this is necessary anyway, since it's a single uint
that will need to be ported to the new Migrations
contract, and the new contract can get it itself by calling last_completed_migration
.
More important might be to let the owner
set a new owner
, so that people aren't stuck with the first key they use to deploy it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good points. The idea is that you can deploy a different Migrations contract as long as it contains the same signature as the old one for two specific functions, setCompleted()
and last_completed_migration()
, the latter provided by solidity. If this is true, the implementation of the new one does not matter: during an upgrade, we'll just treat it like its the old one and call its setCompleted()
function, which must exist. The data
field that gets generated for that transaction should be the same.
You have very good points though. This is actually something I added initially, but have not tested. Perhaps I can write a test for it.
I'm not intimately familiar with DappleScript's deployment features, so I don't have enough information to answer this. However, I would say that both Dapple and Truffle are aiming to solve the same issues, albeit in different ways. Having your deployment language the same language as your tests and the same language as your frontend, in my opinion, is a big win. This means you can use the same code constructs throughout.
I'll see if I can cook something up.
Only contracts that you deploy are on chain, one of those contracts being the default Migrations contract. From there a single uint tracks which migrations have been run and which haven't, based on the prefix of migration file. |
Note: Docs for the |
Closing this ticket. I've hand merged it into the |
Migrations: TIP 1
This pull request adds migration support as per TIP #1.
Installation
New Features
Including the migrations feature, this branch also includes:
Contract.deployed_address
no longer exists. UseContract.address
, orContract.deployed().address
.environments
directory, in favor of "network" configuration (see below). Built contracts now exist in abuild/contracts
directory, one per contract file (as opposed to one per contract file per environment) and the build artifacts from each network are stored inside that built file.deploy
configuration completely removed, as well asafter_deploy
. Both featuresets are superseded by this PR.truffle exec
must be written in a different structure, exporting a function that accepts a callback.truffle console
. You can now type commands inside it as if you were running truffle on the command line. For instance,migrate --reset
within the console will rerun your migrations and immediately make your contract abstractions available for use within the same console.What are Migrations?
The migrations feature is a "script management" tool that allows you to perform scripted contract deployment. It's built under the assumption that your application's contract structure will evolve over time, and that you'll need a solution to manage this evolution.
Deployment scripts -- otherwise called "migrations" -- exist within the
migrations
directory. They are Javascript files that are given a specific naming scheme, prefixed by a number, that allows for their execution history to be recorded. Over time, your projects evolution will require you to write new migration scripts, and recording their execution allows you to run only the scripts necessary for any given network.Migration history is recorded on-chain through a special Migrations contract. This contract is required by Truffle, and must exist within your
contracts
directory in order to use the migrations feature. You can edit the Migrations contract at will so long as it maintains the same interface for recording and reading the migration state. View the default migrations contract.Example/Howto
Let's create the first migration together. This portion assumes you're migrating an older Truffle project to this version. You can also run
truffle init
to get a new project with migrations included.Create a migrations directory:
Then create the initial migration. For most projects, this initial migration will be the same. Note that this migration deploys the migrations contract:
File:
migrations/1_initial_migration.js
Now, add the migrations contract to your project and place it within the
contracts
directory.After adding it to your project, run
truffle migrate
. You should see the following output:You can create further migrations by adding new Javascript files to your
migrations
directory with prefixes in ascending order, having them export a function similar to the one above. Since we've already executed the first migration on chain,truffle migrate
will only execute any migrations with a higher prefix (i.e., higher than 1) the next time it is run. If no new migration files exist, it won't do anything at all.The Deployer
You'll notice we deployed the Migrations contract using the
deployer
object. This is a special object provided by Truffle that will help you perform the mundane aspects of deployment, such as saving deployed contract addresses for future use. Note that like Truffle tests, your contract abstractions (e.g., theMigrations
object, in the example above) are provided to your migration files for you.Your migration file will use the deployer to queue deployment tasks. As such, you can write deployment tasks synchronously and they'll be executed in the correct order:
Alternatively, each function on the deployer can be used as a Promise, to queue up deployment tasks that depend on the execution of the previous task:
It is possible to write your deployment as a single promise chain if you find that to be more clear.
Deployer API
The deployer has many functions available to simplify your deployment.
deploy(contract, [arg1, [arg2, ...]])
Deploy a specific contract, specified by the contract object, with optional constructor arguments. This is useful for singleton contracts, such that only one instance of this contract exists for your dapp. This will set the address of the contract after deployment (i.e.,
Contract.address
will equal the newly deployed address), and it will override any previous address stored.You can optionally pass an array of contracts, or an array of arrays, to speed up deployment of multiple contracts.
Note that
deploy
will automatically link any required libraries to the contracts that are being deployed, if addresses for those libraries are available. You must deploy your libraries first before deploying a contract that depends on one of those libraries.Examples:
deployer.link(library, destinations)
Link an already-deployed library to a contract or multiple contracts.
destinations
can be a single contract or an array of multiple contracts. If any contract within the destination doesn't rely on the library being linked, the deployer will ignore that contract.This is useful for contracts that you don't intend to deploy (i.e. are not singletons) yet will need linkage before being used in your dapp.
Example:
autolink(contract)
Link all libraries that
contract
depends on to that contract. This requires that all librariescontract
depends on have already been deployed or were queued for deployment in a previous step.Example:
then(function() {...})
Just like a promise, run an arbitrary deployment step.
Example:
new(contract, [arg1, arg2, ...])
Exactly like
deploy()
except that this function does not save the contract address. This is purely syntactic sugar, but will be recorded as a deployment step in the logs. Might be removed.An alternative is to use
Contract.new()
within a then block. See example above.exec(pathToFile)
Execute a file meant to be run with
truffle exec
as part of the deployment. This takes the role of theafter_deploy
configuration available in the previous version of Truffle and can be used conditionally depending on the network -- adding demo data when deploying to the staging or test environment, for instance.Example:
Networks
This version of Truffle introduced the idea of networks, to replace environments. Networks are now configured within your main
truffle.js
file, and this configuration is not required. Network artifacts are saved within a single.sol.js
file per contract, and you can require this.sol.js
file in your frontend deploying your frontend only once. Using the features of ether-pudding 3.0, this allows you to only deploy your frontend once, and your contract abstractions will detect the currently running network and use the correct artifacts accordingly. TL;DR: These new changes allow a single frontend deployment to facilitate multiple networks, including the live network, the morden network, etc.Configuration
truffle.js
The above configuration tells Truffle that you have a network called "live" that will be used when the network id is
1
. You can also specify optional parameters, detailed above. When the contract abstractions detect a connected client with the network id of1
, the saved contract artifacts for that network, like the deployed address, will be used.By default, Truffle uses a network with the title "default" if none are specified. You can successfully deploy a Truffle application using the "default" network and not configuring any other networks, but in practice this will rarely be the case (think, "default" is used for development while "live" is used when the contract is ready for deployment).
Usage
To deploy to a network, specify it by name:
This will run your migrations against the network specified. Note that if you've never ran
truffle migrate
on this network before, it will run your migrations from the beginning.Conditionally migrating depending on network
You can run separate deployment steps conditionally based on the network chosen. To do so, add a second parameter to your migration:
3_some_migration.js
Conclusion
The migrations feature is a work in progress. For instance, it was heavily inspired by Active Record Migrations from Rails, but you won't find any mention of "up" and "down" directions within our implementation. This is because Ethereum dapps exist within a different environment, and require different deployment needs. These needs we'll continue to explore, and like your dapp's deployment structure, will consistently evolve. Your feedback is most welcome.